import sys

from odoo import api, fields, models
from odoo.exceptions import UserError
from ...functions.marketing_reward_schedule_func import MarketingRewardScheduleFunc

RULE_CATEGORY = [('draft', '草稿'), ('done', '完成'), ('discard', '弃用'), ('await', '待生效')]


# todo 邀请计划的自动失效
# todo 邀请计划的历史校验
class RpInvitePlan(models.Model):
    _name = 'rp.invite.plan'
    _description = '邀请计划'

    name = fields.Char(string='名称', copy=False)
    code = fields.Char(string='编码', default='RP_IP/****', copy=False)

    # 用户限制条件
    rp_special_customer_tag = fields.Boolean(string='特殊客户', default=False, copy=False)
    rp_customer_level_tag = fields.Boolean(string='客户等级', default=False, copy=False)
    rp_customer_level_ids = fields.Many2many(string='客户等级', comodel_name='rp.customer.level', column1='rp_invite_plan_id', column2='rp_customer_level_id',
                                             relation='rp_invite_plan_customer_level_rel', compute='depends_on_level_tag', store=True, copy=False)
    rp_customer_category_tag = fields.Boolean(string='客户分类', default=False, copy=False)
    rp_customer_category_ids = fields.Many2many(string='客户分类', comodel_name='rp.customer.category', column1='rp_invite_plan_id', column2='rp_customer_category_id',
                                                relation='rp_invite_plan_customer_category_rel', compute='depends_on_category_tag', store=True, copy=False)
    # 奖励规则
    actual_customer_category_ids = fields.Many2many(string='分类', related='rp_reward_incentive_rule_id.customer_category_ids', copy=False)
    actual_customer_level_ids = fields.Many2many(string='等级', related='rp_reward_incentive_rule_id.customer_level_ids', copy=False)

    # Note 方便后续规则自动失效的状态查看
    rule_state = fields.Selection(string='规则状态', related='rp_reward_incentive_rule_id.rule_state', copy=False)
    rp_reward_incentive_rule_id = fields.Many2one(string='奖励规则', comodel_name='rp.reward.incentive.rule', copy=False, required=True)
    generated_person_ids = fields.Many2many(string='客户', comodel_name='rp.customer', column1='rp_invite_plan_id', column2='rp_customer_id', relation='rp_invite_plan_customer_rel',
                                            copy=False, help='已经生成该规则邀请码的客户')
    validate_begin_date = fields.Date(string='开始时间', related='rp_reward_incentive_rule_id.start_date_validate', store=True, copy=False)
    validate_end_date = fields.Date(string='结束时间', related='rp_reward_incentive_rule_id.end_date_validate', store=True, copy=False)

    # Note 筛选出的规则一定是位于今天或之后的
    code_generator_tag = fields.Boolean(string='标志', compute='depends_on_plan_state', default=False, copy=False)
    plan_state = fields.Selection(string='状态', selection=[('drift', '草稿'), ('enable', '激活'), ('discard', '失效')], default='drift', copy=False)

    @api.model_create_multi
    def create(self, vals_list):
        for vals in vals_list:
            vals['code'] = self.env['ir.sequence'].next_by_code('rp.invite.plan') or '/'
        result_list = super(RpInvitePlan, self).create(vals_list)
        for record in result_list:
            record.write({'plan_state': 'enable'})
            record.depends_on_plan_state()
        return result_list

    """
    Depend
    """

    @api.depends('rp_customer_level_tag')
    def depends_on_level_tag(self):
        """
        等级标志取消默认为所有等级
        """
        for record in self:
            if record.rp_customer_level_tag:
                record.write({'rp_customer_level_ids': [(6, 0, record.rp_customer_level_ids.ids)]})
            else:
                customer_level_ids = self.env['rp.customer.level'].search([])
                record.write({'rp_customer_level_ids': [(6, 0, customer_level_ids.ids)]})

    @api.depends('rp_customer_category_tag')
    def depends_on_category_tag(self):
        """
        分类等级取消默认为所有分类
        """
        for record in self:
            if record.rp_customer_category_tag:
                record.write({'rp_customer_category_ids': [(6, 0, record.rp_customer_category_ids.ids)]})
            else:
                customer_category_ids = self.env['rp.customer.category'].search([])
                record.write({'rp_customer_category_ids': [(6, 0, customer_category_ids.ids)]})

    @api.depends('plan_state')
    def depends_on_plan_state(self):
        for record in self:
            if record.plan_state == 'drift':
                self.code_generator_tag = False
            # 弃用但是未创建邀请码
            if record.plan_state == 'enable' and record.code_generator_tag:
                self.code_generator_tag = True
            if record.plan_state == 'enable' and not record.code_generator_tag:
                self.generator_invite_code(record)
            if record.plan_state == 'discard':
                # NOTE 邀请计划失效->邀请任务失效由自动任务完成
                self.code_generator_tag = False

    """
    Onchange
    """

    @api.onchange('rp_customer_category_tag', 'rp_customer_level_tag')
    def onchange_on_level_category(self):
        if self.rp_customer_category_tag:
            self.update({'rp_customer_category_ids': False})
        if self.rp_customer_level_tag:
            self.update({'rp_customer_level_ids': False})

    @api.onchange('rp_customer_category_ids', 'rp_customer_level_ids')
    def onchange_on_level_category_ids(self):
        """
        自定义Domain
        """
        domain_list = []
        # 清除选中的奖励规则
        self.update({'rp_reward_incentive_rule_id': False})
        if len(self.rp_customer_category_ids) > 0:
            domain_list.append(('customer_category_ids', 'in', self.rp_customer_category_ids.ids))
        if len(self.rp_customer_level_ids) > 0:
            domain_list.append(('customer_level_ids', 'in', self.rp_customer_level_ids.ids))
        #  时间范围（筛选出范围包括此刻的规则）
        now_date = fields.Date.today()
        domain_list.append(('start_date_validate', '<=', now_date))
        domain_list.append(('end_date_validate', '>=', now_date))
        domain_list.append(('rule_state', '=', 'done'))
        return {'domain': {'rp_reward_incentive_rule_id': domain_list}}

    """
    Button Function
    """

    def generator_invite_code_by_manual(self):
        for record in self:
            self.generator_invite_code(record)

    """
    Common Function
    """

    def generator_invite_code(self, record):
        """
        生成邀请码
        """
        # NOTE 需要根据奖励规则的客户分类和等级生成邀请码
        domain_list = []
        if len(record.rp_customer_category_ids) > 0:
            domain_list.append(('rp_customer_category', 'in', record.rp_reward_incentive_rule_id.customer_category_ids.ids))
        if len(record.rp_customer_level_ids) > 0:
            domain_list.append(('customer_level', 'in', record.rp_reward_incentive_rule_id.customer_level_ids.ids))
        rp_customer_ids = self.env['rp.customer'].search(domain_list)
        if len(rp_customer_ids) == 0:
            # NOTE 符合条件的用户不存在则标志为False
            record.code_generator_tag = False
        else:
            if set(record.generated_person_ids.ids) == set(rp_customer_ids.ids):
                raise UserError('该规则已经生成邀请码！')
            for person in rp_customer_ids:
                # NOTE 生成邀请码
                MarketingRewardScheduleFunc(self.env).generator_invite_task(record.rp_reward_incentive_rule_id, person)
            # NOTE 填充用户，后续可能该规则下的用户新增
            record.write({'generated_person_ids': [(6, 0, set(record.generated_person_ids.ids + rp_customer_ids.ids))]})
            record.code_generator_tag = True


"""
 奖励规则    【1 有效期包括今天的时间段  2 未来的时间段】 创建即生效，自动任务自动过期规则
"""


class RpRewardIncentiveRule(models.Model):
    _name = 'rp.reward.incentive.rule'
    _description = '奖励规则'
    _inherit = ['mail.thread', 'mail.activity.mixin']
    _rec_name = 'code'

    name = fields.Char(string='名称', copy=False, required=True)
    code = fields.Char(string='序列号', default='RP_RIU/****', copy=False, required=True)
    is_category_tag = fields.Boolean(string='客户分类', default=False, copy=False, required=True)
    is_level_tag = fields.Boolean(string='客户等级', default=False, copy=False, required=True)
    customer_category_ids = fields.Many2many(string='客户分类', comodel_name='rp.customer.category', column1='reward_incentive_rule_id', column2='customer_category_id',
                                             relation='incentive_rule_customer_category_rel', copy=False, tracking=True)
    customer_level_ids = fields.Many2many(string='客户等级', comodel_name='rp.customer.level', column1='reward_incentive_rule_id', column2='customer_category_id',
                                          relation='incentive_rule_customer_level_rel', copy=False, tracking=True)
    invite_category_id = fields.Many2one(string='邀请类型', comodel_name='rp.invite.category', required=True, copy=False, tracking=True)
    coupon_tag = fields.Boolean(string='优惠券标志', required=True, default=False)
    cash_back_tag = fields.Boolean(string='返现标志', required=True, default=False)
    rp_reward_coupon_line_ids = fields.One2many(string='优惠券明细', comodel_name='rp.reward.for.coupon.line', inverse_name='rp_reward_incentive_rule_id', copy=False, tracking=True)
    rp_selected_coupon_line_ids = fields.Many2many(string='已选优惠券', comodel_name='rp.reward.for.coupon.line', column1='reward_for_incentive_rule_id', column2='rp_reward_for_coupon_line_id',
                                                   relation='rp_reward_rule_and_reward_coupon_line_rel', copy=False, tracking=True)
    rp_reward_gradient_template_ids = fields.One2many(string='奖励梯度', comodel_name='rp.reward.gradient.template', inverse_name='rp_reward_incentive_rule_id', copy=False, tracking=True)
    invited_cash_back_price = fields.Float(string='被邀请者返现', copy=False, tracking=True)
    currency_id = fields.Many2one(string='货币单位', comodel_name='res.currency', default=lambda self: self.env.company.currency_id)
    validate_number = fields.Char(string='有效时间', compute='depends_date', copy=False, store=True)
    start_date_validate = fields.Date(string='开始时间', required=True, copy=False, tracking=True)
    end_date_validate = fields.Date(string='结束时间', required=True, copy=False, tracking=True)
    rule_state = fields.Selection(string='状态', selection=RULE_CATEGORY, default=RULE_CATEGORY[0][0], store=True, copy=False)
    validate_flag = fields.Boolean(string='有效', compute='depend_on_rule_state', copy=False, store=True)

    @api.model_create_multi
    def create(self, vals_list):
        history_ids = self.env['rp.reward.incentive.rule']
        for vals in vals_list:
            vals['code'] = self.env['ir.sequence'].sudo().next_by_code('rp.reward.incentive.rule') or '/'
            # 填充数据（未勾选则默认为全部）
            if vals.get('is_category_tag') is False:
                category_ids = self.env['rp.customer.category'].search([])
                vals['customer_category_ids'] = [(6, 0, category_ids.ids)]
            if vals.get('is_level_tag') is False:
                level_ids = self.env['rp.customer.level'].search([])
                vals['customer_level_ids'] = [(6, 0, level_ids.ids)]
            # 校验历史规则
            history_id = MarketingRewardScheduleFunc(self.env).handle_history_record_for_write(invite_category_id=vals.get('invite_category_id'), values=vals)
            history_ids |= history_id
        results = super(RpRewardIncentiveRule, self).create(vals_list)
        #   校验成功修改历史记录状态
        for record in history_ids:
            record.write({'rule_state': 'discard'})
        for result in results:
            now = fields.Date.today()
            if result.start_date_validate > now:
                result.write({'rule_state': 'await'})
            else:
                result.write({'rule_state': 'done'})
        return results

    def write(self, values):
        validate_flag = values.get('validate_flag')
        rule_state = values.get('rule_state')
        if validate_flag is not None or rule_state is not None:
            # 操作为失效规则，或是在已失效规则上修改部分字段，无需历史校验
            if rule_state == 'discard' or not validate_flag:
                return super(RpRewardIncentiveRule, self).write(values)
        if not self.is_category_tag or values.get('is_category_tag') is False:
            category_ids = self.env['rp.customer.category'].search([])
            values['customer_category_ids'] = [(6, 0, category_ids.ids)]
        if not self.is_level_tag or values.get('is_level_tag') is False:
            level_ids = self.env['rp.customer.level'].search([])
            values['customer_level_ids'] = [(6, 0, level_ids.ids)]
        # 校验历史记录
        invite_category_id = values.get('invite_category_id') if values.get('invite_category_id') else self.invite_category_id.id
        history_ids = MarketingRewardScheduleFunc(self.env).handle_history_record_for_write(invite_category_id=invite_category_id, values=values, id=self.id)
        # 为了防止编辑弃用状态的记录，造成其他有冲突记录的失效问题
        if history_ids and self.rule_state != 'discard':
            for record in history_ids:
                record.write({'rule_state': 'discard'})
        result = super(RpRewardIncentiveRule, self).write(values)
        return result

    def name_get(self):
        result_list = []
        for record in self:
            if self._context.get('show_display'):
                result_list.append((record.id, f'{record.name}[{record.code}]'))
            else:
                result_list.append((record.id, record.name))
        return result_list

    @api.constrains('start_date_validate', 'end_date_validate', 'validate_flag', 'cash_back_tag')
    def _check_data(self):
        for record in self:
            if all([record.start_date_validate, record.end_date_validate]):
                difference = (record.end_date_validate - record.start_date_validate).days
                if difference < 0:
                    raise UserError('结束时间不能早于开始时间！')
                if (record.end_date_validate - fields.date.today()).days < 0 and record.validate_flag:
                    raise UserError('您填写的结束时间位于今天之前，请填写有效的时间！')
            # 返现金额需要有效
            if record.cash_back_tag:
                if record.invited_cash_back_price == 0.0:
                    raise UserError('请填写有效金额！')

    @api.depends('start_date_validate', 'end_date_validate')
    def depends_date(self):
        for record in self:
            if all([record.start_date_validate, record.end_date_validate]):
                difference = (record.end_date_validate - record.start_date_validate).days
                if difference < 0:
                    raise UserError('结束时间不能早于开始时间！')
                self.update({'validate_number': str(difference) + ' ' + '天'})

    @api.onchange('coupon_tag')
    def _onchange_domain_relation_field_id(self):
        if self.coupon_tag:
            if len(self.rp_reward_coupon_line_ids.ids) > 0:
                default_domain = [('rp_reward_incentive_rule_id', '=', self._origin.id)]
            else:
                # 返回一个不可能有结果的限制域
                default_domain = [('rp_reward_incentive_rule_id', '=', sys.maxsize)]
            return {'domain': {'rp_selected_coupon_line_ids': default_domain}}

    @api.onchange('cash_back_tag', 'coupon_tag')
    def onchange_tag(self):
        if self.cash_back_tag is False:
            self.update({'invited_cash_back_price': 0.0})
        if self.coupon_tag is False:
            self.update({'rp_selected_coupon_line_ids': False})

    @api.onchange('is_category_tag', 'is_level_tag')
    def onchange_customer_tag(self):
        if self.is_category_tag:
            self.update({'customer_category_ids': False})
        if self.is_level_tag:
            self.update({'customer_level_ids': False})

    @api.depends('rule_state')
    def depend_on_rule_state(self):
        for record in self:
            if record.rule_state == 'draft':
                record.write({'validate_flag': False})
            if record.rule_state == 'discard':
                record.write({'validate_flag': False})
            if record.rule_state == 'done':
                record.write({'validate_flag': True})
            if record.rule_state == 'await':
                record.write({'validate_flag': True})

    def turn_expired(self):
        """
        失效规则
        """
        self.ensure_one()
        self.update({'rule_state': 'discard', 'validate_flag': False})

    def turn_enable(self):
        """
        生效规则
        """
        self.ensure_one()
        # 日期校验
        if (self.end_date_validate - fields.date.today()).days < 0:
            raise UserError('本规则已过期，不可启用！')
        # 校验历史记录
        history_ids = MarketingRewardScheduleFunc(self.env).handle_history_record(invite_category_id=self.invite_category_id.id, customer_category_ids=self.customer_category_ids.ids,
                                                                                  customer_level_ids=self.customer_level_ids.ids, id=self.id)
        #   校验成功修改历史记录状态
        if history_ids:
            for record in history_ids:
                record.write({'rule_state': 'discard'})
        self.update({'rule_state': 'done', 'validate_flag': True})


class RpRewardGradientTemplate(models.Model):
    _name = 'rp.reward.gradient.template'
    _description = '奖励梯度'
    _order = 'invite_number'

    rp_reward_incentive_rule_id = fields.Many2one(string='关联主表', ondelete='cascade', comodel_name='rp.reward.incentive.rule', copy=False, required=True)
    invite_number = fields.Integer(string='邀请人数', copy=False, required=True)
    coupon_tag = fields.Boolean(string='优惠券标志', required=True)
    cash_back_tag = fields.Boolean(string='返佣标志', required=True)
    currency_id = fields.Many2one(string='货币单位', comodel_name='res.currency', default=lambda self: self.env.company.currency_id)
    rp_selected_coupon_line_ids = fields.Many2many(string='已选优惠券', comodel_name='rp.reward.for.coupon.line', column1='reward_gradient_template_id',
                                                   column2='rp_reward_for_coupon_line_id', relation='reward_gradient_template_and_reward_coupon_line_rel', copy=False)
    invite_cash_back_price = fields.Float(string='邀请者返现', copy=False)

    @api.constrains('coupon_tag', 'cash_back_tag', 'invite_number')
    def _constraint_values(self):
        for record in self:
            # 有效性判断
            if record.coupon_tag and len(record.rp_selected_coupon_line_ids) == 0:
                raise UserError('请选择优惠券后重试！')
            if record.cash_back_tag and record.invite_cash_back_price == 0:
                raise UserError('请填写有效金额！')
            if record.invite_number <= 0:
                raise UserError('请填写有效的人数！')
            # 合法性判断
            invite_number_list = record.rp_reward_incentive_rule_id.rp_reward_gradient_template_ids.mapped(lambda r: r.invite_number)
            if len(set(invite_number_list)) != len(invite_number_list):
                raise UserError('您重复配置相同邀请人数的记录！')


class RpRewardForCouponLine(models.Model):
    _name = 'rp.reward.for.coupon.line'
    _description = '优惠券明细'
    _sql_constraints = [('template_amount_constraints', 'UNIQUE (rp_reward_incentive_rule_id,rp_coupon_template_id,amount)', '同一个规则模板和数量必须唯一')]
    _rec_name = 'name_display'

    name_display = fields.Char(string='名称', compute='depend_coupon_template_amount', copy=False)
    rp_reward_incentive_rule_id = fields.Many2one(string='关联主表', comodel_name='rp.reward.incentive.rule', copy=False)
    rp_coupon_template_id = fields.Many2one(string='优惠券模板', comodel_name='rp.marketing.coupon.template', domain=[('state', '=', 'enable')], copy=False)
    amount = fields.Integer(string='数量', copy=False)

    @api.constrains('amount')
    def _constraint_amount(self):
        for record in self:
            if record.amount <= 0:
                raise UserError('请填写有效的优惠券数量！')

    @api.depends('rp_coupon_template_id', 'amount')
    def depend_coupon_template_amount(self):
        for record in self:
            if all([record.rp_coupon_template_id, record.amount]):
                record.update({'name_display': f'{record.rp_coupon_template_id.name} *{record.amount}'})
            else:
                record.update({'name_display': 'NEW'})
